package cn.edu.neu.lab603.cloudeval.strategy

import cn.edu.neu.lab603.cloudeval.entity._
import com.typesafe.scalalogging.LazyLogging

import scala.collection.mutable

/** 一种支持过载的分配策略。
  *
  * Created by yfwz100 on 16/9/24.
  */
class SimpleOverloadAllocationStrategy(enlargeFactor: Double = 2) extends AllocationStrategy with LazyLogging {
  type VM = VirtualMachine
  type Host = PhysicalMachine

  override def initDataCenterLayout(dataCenter: DataCenter): Unit = {
    dataCenter.pmList.foreach(_.boot())
  }

  override def allocateVMForCloudlet(cloudlet: CloudTask, dataCenter: DataCenter): VM = {
    for (pm <- dataCenter.pmList.sortWith((a, b) => a.getVMList.size < b.getVMList.size)) {
      val remainResMap = pm.getVMList.foldLeft(mutable.HashMap[String, Double](pm.capacity.map(p => (p._1, p._2 * enlargeFactor)).toSeq: _*)) { (m, it) =>
        it.getSpecResMap.foreach { case (res, v) =>
          m.update(res, m.getOrElse(res, 0.0) - v)
        }
        m
      }
      if (pm.getStatus == PhysicalMachine.Status.ON
        && remainResMap.forall(p => cloudlet.resMap(p._1).getMax <= p._2)) {
        try {
          val vm = pm.createVirtualMachine(cloudlet.getResSpecMap)
          vm.setCloudlet(cloudlet)
          cloudlet.setExecutionContext(vm)
          return vm
        } catch {
          case _: NoEnoughResForVM => logger.trace(s"No enough resource in Host#${pm.id}")
        }
      } else {
        pm.boot()
      }
    }
    throw new NoHostToAllocateException
  }

  override def deallocateVMForCloudlet(cloudlet: CloudTask, dataCenter: DataCenter): Unit = {
    val vm = cloudlet.getExecutionContext
    if (vm != null) {
      val host = vm.host
      host.deallocateVirtualMachine(cloudlet)

      if (host.getStatus == PhysicalMachine.Status.ON && host.getVMList.forall(_.isIdle)) {
        host.shutdown()
      }
    } else {
      throw new RuntimeException("No VM found.")
    }
  }
}
